#include <tsin/library/BulletEmergingDresser.hpp>

#include <tsin/library/BulletEvent.hpp>

#include <tsin/runtime/GameRuntime.hpp>
#include <tsin/runtime/BulletRuntime.hpp>
#include <tsin/display/Display.hpp>
#include <tsin/display/Image.hpp>
#include <tsin/display/DecoratedImage.hpp>
#include <tsin/util/Resolution.hpp>
#include <tsin/util/Logger.hpp>
#include <tsin/config/Config.hpp>

#include <cstring>

using namespace tsin;

class LIBRARY_DLLEXPORT BulletEmergingEvent : public BulletEvent {
public:
        BulletEmergingEvent() : counter(-1), prevStage(BULLET_STAGE_UTERO) {}

        void init(ImageSeriesId series, BulletShape shape, BulletColor color, BlendStyle blendStyle)
        {
                this->series = series;
                this->shape = shape;
                this->color = color;
                this->blendStyle = blendStyle;
                res = currentGame->config.resolution;
                display = &currentGame->display;
        }

        virtual void reinit()
        {
                ImageSeriesId arg1 = static_cast<ImageSeriesId>(arguments[0]->getInt());
                BulletShape   arg2 = static_cast<BulletShape>(arguments[1]->getInt());
                BulletColor   arg3 = static_cast<BulletColor>(arguments[2]->getInt());
                BlendStyle    arg4 = static_cast<BlendStyle>(arguments[3]->getInt());

                switch(nrArguments) {
                case 4:
                        if (strcmp(desp, "EEEE") == 0)
                                init(arg1, arg2, arg3, arg4);
                        break;
                }
        }

        virtual bool trigger(BulletRuntime &bullet, void *)
        {
                if (bullet.stage != prevStage) {
                        prevStage = bullet.stage;
                        return true;
                }
                if (bullet.stage == BULLET_STAGE_BEFALL || bullet.stage == BULLET_STAGE_VANISHING)
                        return true;
                return false;
        }

        virtual BulletEvent *handle(BulletRuntime &bullet, void *)
        {
                switch (bullet.stage) {
                case BULLET_STAGE_BEFALL:
                        startBefalling(bullet);
                        break;
                case BULLET_STAGE_LIVE:
                        startLiving(bullet);
                        break;
                case BULLET_STAGE_VANISHING:
                        startVanishing(bullet);
                        break;
                default:
                        break;
                }

                return this;
        }

private:
        Display *display;

        ImageSeriesId series;
        BulletShape shape;
        BulletColor color;
        BlendStyle blendStyle;
        StandardResolution res;

        BulletLifeStage prevStage;
        int counter;
        float ratio, alpha;

        void startBefalling(BulletRuntime &bullet)
        {
                static const float MAX_RATIO = 2.5f, STEP_RATIO = 0.1f;
                static const float MIN_ALPHA = 0.3f, STEP_ALPHA = 0.05f;

                if (counter >= 0) {
                        float fw = bullet.w / ratio, fh = bullet.h / ratio;
                        counter --;
                        ratio -= STEP_RATIO;
                        alpha += STEP_ALPHA;
                        bullet.w = fw * ratio;
                        bullet.h = fh * ratio;
                        bullet.image->setRGBA(1.0f, 1.0f, 1.0f, alpha);
                        if (counter < 0)
                                bullet.stage = BULLET_STAGE_LIVE;
                        return;
                }

                ImageInfo info;
                BuiltinImages::inquiry(series, shape, color, info);
                switch(info.w) {
                case 16:
                        counter = 15;
                        break;
                case 32:
                        counter = 10;
                        break;
                default:
                        counter = 1;
                }
                ratio = MAX_RATIO;
                alpha = MIN_ALPHA;

                BuiltinImages::inquiry(TOUHOU_BIG_BULLET_SERIES, FADING, color, info);
                Image *image = display->openImage(info.path, res);

                bullet.image = new DecoratedImage(*image);
                bullet.w = Resolution::pixel2rangeX(res, info.w) * ratio;
                bullet.h = Resolution::pixel2rangeY(res, info.h) * ratio;
                bullet.image->init(&info);
                bullet.image->setBlendStyle(blendStyle);
                bullet.image->setRGBA(1.0f, 1.0f, 1.0f, alpha);
                image->unref();
        }

        void startLiving(BulletRuntime &bullet)
        {
                delete bullet.image;

                ImageInfo info;
                BuiltinImages::inquiry(series, shape, color, info);
                Image *image = display->openImage(info.path, res);

                bullet.image = new DecoratedImage(*image);
                bullet.w = Resolution::pixel2rangeX(res, info.w);
                bullet.h = Resolution::pixel2rangeY(res, info.h);
                bullet.image->init(&info);
                bullet.image->setBlendStyle(blendStyle);
                bullet.image->setRGBA(1.0f, 1.0f, 1.0f, 1.0f);
                image->unref();
        }

        void startVanishing(BulletRuntime &bullet)
        {
                static const float MIN_RATIO = 1.0f, STEP_RATIO = 0.05f;
                static const float MAX_ALPHA = 1.0f, STEP_ALPHA = 0.1f;

                if (counter >= 0) {
                        float fw = bullet.w / ratio, fh = bullet.h / ratio;
                        counter --;
                        ratio += STEP_RATIO;
                        alpha -= STEP_ALPHA;
                        bullet.w = fw * ratio;
                        bullet.h = fh * ratio;
                        bullet.image->setRGBA(1.0f, 1.0f, 1.0f, alpha);
                        if (counter < 0)
                                bullet.stage = BULLET_STAGE_DIED;
                        return;
                }

                ratio = MIN_RATIO;
                alpha = MAX_ALPHA;
                counter = 10;
        }
};

BulletEmergingDresser::BulletEmergingDresser()
{
        series = TOUHOU_SMALL_BULLET_SERIES;
        shape = SPELLCARD;
        color = BLUE;
}

BulletEmergingDresser::~BulletEmergingDresser()
{
}

DynamicObject *BulletEmergingDresser::copy()
{
        return DynamicObject::copy(new BulletEmergingDresser);
}

void BulletEmergingDresser::init(ImageSeriesId series, BulletShape shape, BulletColor color, BlendStyle blendStyle)
{
        this->series = series;
        this->shape = shape;
        this->color = color;
        this->blendStyle = blendStyle;
}

void BulletEmergingDresser::reinit()
{
        ImageSeriesId arg1 = static_cast<ImageSeriesId>(arguments[0]->getInt());
        BulletShape   arg2 = static_cast<BulletShape>(arguments[1]->getInt());
        BulletColor   arg3 = static_cast<BulletColor>(arguments[2]->getInt());
        BlendStyle    arg4 = static_cast<BlendStyle>(arguments[3]->getInt());

        switch(nrArguments) {
        case 3:
                if (strcmp(desp, "EEE") == 0)
                        init(arg1, arg2, arg3);
                break;
        case 4:
                if (strcmp(desp, "EEEE") == 0)
                        init(arg1, arg2, arg3, arg4);
                break;
        }
}

void BulletEmergingDresser::reset()
{
}

void BulletEmergingDresser::dress(BulletRuntime *bullet)
{
        BulletEmergingEvent *event = new BulletEmergingEvent();
        event->init(series, shape, color, blendStyle);
        bullet->addBulletEvent(event);
}

extern "C" LIBRARY_DLLEXPORT DynamicObject *bindEmergingDresser()
{
        return new BulletEmergingDresser;
}
